package com.ice.common.codec;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.util.TypeUtils;

import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author zjn
 */
public final class IceLongCodec implements ObjectSerializer, ObjectDeserializer {

  private static final IceLongCodec INSTANCE = new IceLongCodec();

  public static IceLongCodec getInstance() {
    return INSTANCE;
  }

  @Override
  public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) {
    SerializeWriter out = serializer.out;

    if (object == null) {
      out.writeNull(SerializerFeature.WriteNullNumberAsZero);
    } else {
      long value = (Long) object;
      out.writeLong(value);
      out.write('L');
    }
  }

  @Override
  @SuppressWarnings("unchecked")
  public <T> T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName) {
    final JSONLexer lexer = parser.lexer;

    Long longObject;
    try {
      final int token = lexer.token();
      if (token == JSONToken.LITERAL_INT) {
        long longValue = lexer.longValue();
        lexer.nextToken(JSONToken.COMMA);
        longObject = longValue;
      } else if (token == JSONToken.LITERAL_FLOAT) {
        BigDecimal number = lexer.decimalValue();
        longObject = TypeUtils.longValue(number);
        lexer.nextToken(JSONToken.COMMA);
      } else {
        if (token == JSONToken.LBRACE) {
          JSONObject jsonObject = new JSONObject(true);
          parser.parseObject(jsonObject);
          longObject = TypeUtils.castToLong(jsonObject);
        } else {
          Object value = parser.parse();

          longObject = TypeUtils.castToLong(value);
        }
        if (longObject == null) {
          return null;
        }
      }
    } catch (Exception ex) {
      throw new JSONException("parseLong error, field : " + fieldName, ex);
    }

    return clazz == AtomicLong.class ? (T) new AtomicLong(longObject) : (T) longObject;
  }

  @Override
  public int getFastMatchToken() {
    return JSONToken.LITERAL_INT;
  }
}
